import VNode from "../vdom/vnode.js";
import {
  prepareRender,
  getTemplate2VnodeMap,
  getVnode2TemplateMap,
  getVNodeByTemplate,
  clearMap
} from "./render.js";
import { vmodel } from "./grammer/vmodel.js";
import { vforInit } from "./grammer/vfor.js";
import { mergeAttr } from "../util/ObjectUtil.js";
import { checkVBind } from "./grammer/vbind.js";
import { checkVOn } from "./grammer/von.js";
export function initMount(Due) {
  // 给Due的原型上添加$mount方法
  // vm表示Due对象自己(this)
  Due.prototype.$mount = function (el) {
    let vm = this;
    let rootDom = document.getElementById(el);
    mount(vm, rootDom);
  };
}

export function mount(vm, elm) {
  // 进行挂载
  vm._vnode = constructVNode(vm, elm, null);
  // 进行预备渲染(建立渲染索引，通过模板找vnode，通过vnode找模板(比如data里只有content数据改变了，不会重新遍历整个dom树，而是只改变涉及{{content}}部分的dom树))
  prepareRender(vm, vm._vnode); // 要想获得元素的所有子节点就传入vm的根节点，vm._vnode是虚拟dom树的根节点
  console.log(getTemplate2VnodeMap());
  console.log(getVnode2TemplateMap());
}

export function rebuild(vm, template) {
  // 重新构建节点,这是一个逆向的过程，需要把已经渲染好的节点打回原形然后重新渲染
  let virtualNode = getVNodeByTemplate(template);
  for (let i = 0; i < virtualNode.length; i++) {
    virtualNode[i].parent.elm.innerHTML = "";   // 把原有的节点删了
    virtualNode[i].parent.elm.appendChild(virtualNode[i].elm);  // 把现在的虚拟节点填进去
    let result = constructVNode(vm, virtualNode[i].elm, virtualNode[i].parent);
    virtualNode[i].parent.children = [result];  // 把父级的children重新设置一遍
    clearMap(); // 重新构建模板和虚拟节点对应关系
    prepareRender(vm, vm._vnode)  // 重新建立关系,(注意这里并没有动dom节点，只是更改了对应关系，所以性能很高)
  }
}

function constructVNode(vm, elm, parent) {
  // 深度优先搜索
  let vnode = analysisAttr(vm, elm, parent); // 分析属性与生成虚拟dom方法二合一，如果是v-model指令则只进行属性分析，如果是v-for则返回新的虚拟节点
  if (vnode === undefined) {
    // 如果有v-for指令的话会生成vnode,就不用新创建vnode了
    // 如果vnode为null就新创建vnode
    let children = []; // 子元素
    let text = getNodeText(elm); // 获取文本节点内的文本
    let data = null;
    let nodeType = elm.nodeType;
    let tag = elm.nodeName;
    vnode = new VNode(tag, nodeType, text, elm, children, parent, data);
    if (elm.nodeType === 1 && elm.getAttribute("env")) {
      vnode.env = mergeAttr(vnode.env, JSON.parse(elm.getAttribute("env")));
      // debugger
    } else {
      vnode.env = mergeAttr(vnode.env, parent ? parent.env : {});
    }
  }
  checkVBind(vm, vnode)  // 检查bind语句，需要在上面在生成的vnode之后执行
  checkVOn(vm, vnode) // 检查von语句
  // debugger
  let childs =
    vnode.nodeType === 0 ? vnode.parent.elm.childNodes : vnode.elm.childNodes; // 获得元素的子节点集合,返回NodeList对象
  // 如果是虚拟节点，则使用父级真实节点下的childNodes
  let len = childs.length;
  for (let i = 0; i < len; i++) {
    let childNodes = constructVNode(vm, childs[i], vnode);
    if (childNodes instanceof VNode) {
      // 返回单一节点的时候
      vnode.children.push(childNodes);
      // debugger
    } else {
      // 返回节点数组(使用v-for的时候会出现这种情况)
      vnode.children = vnode.children.concat(childNodes);
    }
  }
  return vnode;
}
function getNodeText(elm) {
  // 只有文本节点才有文本
  return elm.nodeType === 3 ? elm.nodeValue : "";
}
function analysisAttr(vm, elm, parent) {
  if (elm.nodeType === 1) {
    let attrNames = elm.getAttributeNames();
    if (attrNames.indexOf("v-model") > -1) {
      // 带有v-model属性的节点
      // 挂载v-model属性
      vmodel(vm, elm, elm.getAttribute("v-model"));
    }
    if (attrNames.indexOf("v-for") > -1) {
      // 带有v-for属性的节点
      // debugger;
      return vforInit(vm, elm, parent, elm.getAttribute("v-for")); // elm.getAttribute("v-for")为属性的值(形如(key) in list)
    }
  }
}
